package com.niiwoo.civet.trade.listener;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONAware;
import com.alibaba.fastjson.JSONObject;
import com.niiwoo.civet.trade.constant.LoanFlowMqConstant;
import com.niiwoo.civet.trade.constant.MqConstant;
import com.niiwoo.civet.trade.dao.entity.*;
import com.niiwoo.civet.trade.dao.mapper.*;
import com.niiwoo.civet.trade.dto.request.ProjectFlowRequestDTO;
import com.niiwoo.civet.trade.dto.request.loan.ProjectWaitPublishReqDTO;
import com.niiwoo.civet.trade.enums.*;
import com.niiwoo.civet.trade.service.dubbo.loan.core.ISpeedLoanFactory;
import com.niiwoo.civet.trade.service.local.configure.TradeConfigLocalService;
import com.niiwoo.civet.trade.service.local.flowexecute.ProjectFlowExecuteService;
import com.niiwoo.civet.trade.service.local.loan.ReturnProjectRecordService;
import com.niiwoo.civet.trade.service.local.loan.SpeedLoanService;
import com.niiwoo.civet.trade.service.local.project.ProjectLocalService;
import com.niiwoo.civet.trade.service.project.ProjectDubboService;
import com.niiwoo.civet.user.dto.request.AddUserLoanLimitRequestDTO;
import com.niiwoo.civet.user.dto.request.AppButtonDTO;
import com.niiwoo.civet.user.dto.request.FacePlusPortraitAuthRequestDTO;
import com.niiwoo.civet.user.dto.request.UserLetterRequestDTO;
import com.niiwoo.civet.user.dto.response.FacePlusPortraitAuthResponseDTO;
import com.niiwoo.civet.user.enums.*;
import com.niiwoo.civet.user.service.FacePlusPortraitAuthDubboService;
import com.niiwoo.civet.user.service.SendSmsDubboService;
import com.niiwoo.civet.user.service.UserForbidDubboService;
import com.niiwoo.civet.user.service.UserLetterDubboService;
import com.niiwoo.tripod.provider.exception.BizException;
import com.niiwoo.tripod.tiancheng.enums.FunctionCodeEnum;
import com.rabbitmq.client.Channel;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;

/**
 * 消息认证流程MQ处理
 */

@Slf4j
@Component
public class ProjectFlowExecutePortraitAuthListener implements ApplicationContextAware {

    @Autowired
    ProjectFlowExecuteRuntimeMapperExt projectFlowExecuteRuntimeMapperExt;

    @Reference(version = "1.0.0")
    FacePlusPortraitAuthDubboService facePlusPortraitAuthDubboService;

    @Reference(version = "1.0.0")
    private ProjectDubboService projectDubboService;

    @Reference(version = "1.0.0")
    private UserForbidDubboService userForbidDubboService;

    @Autowired
    TradeConfigLocalService tradeConfigLocalService;

    @Autowired
    ProjectMapperExt projectMapperExt;

    @Autowired
    ProjectFlowExecuteService projectFlowExecuteService;

    @Autowired
    SpeedLoanService speedLoanService;

    @Autowired
    ProjectLocalService projectLocalService;

    @Reference(version = "1.0.0")
    SendSmsDubboService sendSmsDubboService;

    @Value("${h5-borrow-detail-url}")
    private String h5BorrowDetailUrl;

    @Reference(version = "1.0.0")
    private UserLetterDubboService userLetterDubboService;

    @Autowired
    LoanBusinessInfoMapperExt loanBusinessInfoMapperExt;

    @Autowired
    ProjectCreditAmountMapperExt projectCreditAmountMapperExt;

    ApplicationContext ctx;

    @Autowired
    ISpeedLoanFactory iSpeedLoanFactory;

    @Autowired
    ReturnProjectRecordService returnProjectRecordService;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private ProjectBackoutBorrowPrizeMapper projectBackoutBorrowPrizeMapper;

    @RabbitListener(queues = LoanFlowMqConstant.Queue.PORTRAIT_AUTH_QUEUE)
    public void onReceive(JSONObject json, Message message, Channel channel) {
        ProjectFlowExecuteRuntime projectFlowExecute = null;
        try {
            MDC.put("traceId", UUID.randomUUID().toString());
            if (json == null) {
                log.error("标的流程###肖像认证###消息为空, tag={}, channel={}", message.getMessageProperties().getDeliveryTag(), channel.getChannelNumber());
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                return;
            }
            ExecuteMQ executeMQ = json.toJavaObject(ExecuteMQ.class);
            if (executeMQ == null || executeMQ.getFlowExecuteId() == null) {
                log.error("标的流程###肖像认证###FlowExecuteId为空, tag={}, channel={}", message.getMessageProperties().getDeliveryTag(), channel.getChannelNumber());
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                return;
            }
            log.info("标的流程###肖像认证###消息, data={}, tag={}, channel={}", json.toJSONString(), message.getMessageProperties().getDeliveryTag(), channel.getChannelNumber());
            projectFlowExecute = projectFlowExecuteRuntimeMapperExt.selectByPrimaryKey(executeMQ.getFlowExecuteId());
            if (projectFlowExecute == null) {
                log.error("标的流程###肖像认证###流程不存在, FlowExecuteId={}", executeMQ.getFlowExecuteId());
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                return;
            }
            // 先查询肖像认证状态
            FacePlusPortraitAuthRequestDTO requestDTO = new FacePlusPortraitAuthRequestDTO();
            requestDTO.setUserId(projectFlowExecute.getBorrowUserId());
            log.info("标的流程###肖像认证###查询请求, FlowExecuteId={}, data={}", projectFlowExecute.getFlowExecuteId(), JSON.toJSONString(requestDTO));
            try {
                FacePlusPortraitAuthResponseDTO authResult = facePlusPortraitAuthDubboService.getAuthInfo(requestDTO);
                log.info("标的流程###肖像认证###查询响应, FlowExecuteId={}, data={}", projectFlowExecute.getFlowExecuteId(), JSON.toJSONString(authResult));
                // 没有肖像认证状态记录， 用户没有提交肖像认证信息
                if (authResult == null) {
                    log.error("标的流程###肖像认证###用户没有任何肖像认证记录, FlowExecuteId={}, userId={}", projectFlowExecute.getFlowExecuteId(), projectFlowExecute.getBorrowUserId());
                    this.handleExceptionFLow(projectFlowExecute, true, String.format("标的流程###肖像认证###用户没有任何肖像认证记录, userId=%s", projectFlowExecute.getBorrowUserId()));
                    channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                    return;
                }
                switch (authResult.getPortraitAuthStatus()) {
                    // 认证通过
                    case PASS_CERTIFIED: {
                        this.handlePassCertified(projectFlowExecute);
                    } break;
                    // 人工审核
                    case NEED_APPROVE:
                    // 未认证
                    case NOT_CERTIFIED:{
                        this.handleNeedApprove(projectFlowExecute);
                    } break;
                    // 不通过
                    case NOT_PASS: {
                        this.handleNotPassOrNotCertified(projectFlowExecute);
                    } break;
                    // 待审核， 可以发起肖像认证
                    case REVIEW: {
                        this.handleReview(projectFlowExecute, authResult.getRemainingTimes());
                    } break;
                    default: {
                        log.error("标的流程###肖像认证###认证状态异常1, FlowExecuteId={}, status={}", projectFlowExecute.getFlowExecuteId(), authResult.getPortraitAuthStatus());
                        this.handleExceptionFLow(projectFlowExecute, true, String.format("标的流程###肖像认证###认证状态异常1, status=%s", authResult.getPortraitAuthStatus()));
                    }
                }
            } catch (BizException e) {
                log.error("标的流程###肖像认证###业务异常, FlowExecuteId={}, code={}, message={}", projectFlowExecute.getFlowExecuteId(), e.getErrorCode(), e.getMessage());
                // 业务异常走人工
                this.handleNeedApprove(projectFlowExecute);
                // this.handleExceptionFLow(projectFlowExecute, false, String.format("标的流程###肖像认证###业务异常, code=%s, message=%s", e.getErrorCode(), e.getMessage()));
            }
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            log.error(String.format("标的流程###肖像认证###处理异常, message=%s, data=%s", e.getMessage(), Optional.ofNullable(json).map(JSONAware::toJSONString).orElse("null")), e);
            if (projectFlowExecute != null) {
                // 异常默认走人工
                this.handleNeedApprove(projectFlowExecute);
                // this.handleExceptionFLow(projectFlowExecute, false, "标的流程###肖像认证###处理异常");
            }
            try {
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            } catch (IOException e1) {
                log.error(String.format("标的流程###肖像认证###处理异常, ack异常, message=%s, data=%s", e.getMessage(), Optional.ofNullable(json).map(JSONAware::toJSONString).orElse("null")), e);
            }
        }
    }

    @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
    public void doWithTransaction(Supplier<Void> s) {
        s.get();
    }

    /**
     * 肖像认证成功处理
     * @param projectFlowExecute
     */
    private void handlePassCertified(ProjectFlowExecuteRuntime projectFlowExecute) {
        log.info("标的流程###肖像认证###肖像认证成功, FlowExecuteId={}, projectId={}", projectFlowExecute.getFlowExecuteId(), projectFlowExecute.getProjectId());
        Project project = projectMapperExt.selectByPrimaryKey(projectFlowExecute.getProjectId());
        if (project == null) {
            log.error("标的流程###肖像认证###肖像认证成功, 找不到标的FlowExecuteId={}, projectId={}", projectFlowExecute.getFlowExecuteId(), projectFlowExecute.getProjectId());
            this.handleExceptionFLow(projectFlowExecute, true, String.format("标的流程###肖像认证###肖像认证成功, 找不到标的projectId=%s", projectFlowExecute.getProjectId()));
            return;
        }
        // 自动审核是否开启
        int autoAuditSwitchStatus = this.getAutoAuditSwitchStatus(project);
        log.info("标的流程###肖像认证###肖像认证成功,自动审核开关状态 FlowExecuteId={}, status={}", projectFlowExecute.getFlowExecuteId(), autoAuditSwitchStatus);
        // 开关开启，则判断额度评分
        if (autoAuditSwitchStatus == 1) {
            // 获取额度评分
            BigDecimal limit = this.getLimitAmount(projectFlowExecute.getProjectId());
            BigDecimal minLoanAmount = this.getMinLoanAmount(projectFlowExecute.getProjectType());
            log.info("标的流程###肖像认证###肖像认证成功, FlowExecuteId={}, 额度评分={}, 标的金额={}", projectFlowExecute.getFlowExecuteId(), limit, project.getHandAmount());
            // 额度评分大于借款金额,进入投资流程
            if (limit.compareTo(project.getHandAmount()) >= 0) {
                log.info("标的流程###肖像认证###肖像认证成功, 上标 FlowExecuteId={}, projectId={}", projectFlowExecute.getFlowExecuteId(), projectFlowExecute.getProjectId());
                // 调用上标 --> 流程结束
                ((ProjectFlowExecutePortraitAuthListener)AopContext.currentProxy()).doWithTransaction(() -> {
//                    PushProjectRequestDTO pushProjectRequestDTO = new PushProjectRequestDTO();
//                    pushProjectRequestDTO.setProjectId(project.getProjectId());
//                    pushProjectRequestDTO.setPushFromEnum(PushProjectRequestDTO.PushFromEnum.AUTO_AUDIT);
//                    iSpeedLoanFactory.adaptive(project).pushProject(pushProjectRequestDTO);
                    ProjectWaitPublishReqDTO projectWaitPublishReqDTO=new ProjectWaitPublishReqDTO ();
                    projectWaitPublishReqDTO.setProjectId(project.getProjectId());
                    projectWaitPublishReqDTO.setPushFromEnum(ProjectWaitPublishReqDTO.PushFromEnum.AUTO_AUDIT);

                    //标的进入待发布列表
                    rabbitTemplate.convertAndSend(MqConstant.PROJECT_WAIT_PUBLISH_EXCHANGE, MqConstant.PROJECT_WAIT_PUBLISH_ROUTING, projectWaitPublishReqDTO);
                    projectFlowExecuteService.successEndProjectFlowExecute(projectFlowExecute.getFlowExecuteId(), LoanNodeTypeEnum.PROJECT_TO_BANK);
                    return null;
                });
            // 额度评分小于借款金额且大于起借金额，则直接让用户确认
            } else if(limit.compareTo(project.getHandAmount()) < 0 && limit.compareTo(minLoanAmount) >= 0 ){
                log.info("标的流程###肖像认证###肖像认证成功, 需要用户确认，FlowExecuteId={}, projectId={}", projectFlowExecute.getFlowExecuteId(), projectFlowExecute.getProjectId());
                // 否则进入人工审核, 5.4.5修改，不走人工审核
                //this.handleNeedApprove(projectFlowExecute);
                Project updateProjectStatusDTO = new Project();
                updateProjectStatusDTO.setSubStatus(ProjectSubStatusEnum.CHECKING_FOR_CONFIRM.getSubStatus());
                updateProjectStatusDTO.setProjectId(project.getProjectId());
                projectMapperExt.updateByPrimaryKeySelective(updateProjectStatusDTO);
                // 创建回退借款红包记录
                if (project.getPrizeId() != null) {
                    ProjectBackoutBorrowPrize backPrize = new ProjectBackoutBorrowPrize();
                    backPrize.setProjectId(project.getProjectId());
                    backPrize.setUserId(project.getBorrowUserId());
                    backPrize.setPrizeId(project.getPrizeId());
                    backPrize.setCreateTime(new Date());
                    projectBackoutBorrowPrizeMapper.insertSelective(backPrize);
                }
                //通过待确认,发送消息
                confirmSendMessage(project);
                projectFlowExecuteService.successEndProjectFlowExecute(projectFlowExecute.getFlowExecuteId(), LoanNodeTypeEnum.AUTO_AUDIT);
            // 额度评分小于起借金额，则直接流程加借款限制
            }else if(limit.compareTo(minLoanAmount) < 0){
                log.info("标的流程###肖像认证###肖像认证成功，天秤额度 < 产品的起借金额，FlowExecuteId={}, projectId={}", projectFlowExecute.getFlowExecuteId(), projectFlowExecute.getProjectId());
                StringBuffer errMsg = new StringBuffer("flowExecuteId=").append(projectFlowExecute.getFlowExecuteId()).append(" projectId=").append(projectFlowExecute.getProjectId()).append("天秤评分卡的额度低于产品的起借金额");
                // 流标
                ProjectFlowRequestDTO projectFlowRequestDTO = new ProjectFlowRequestDTO();
                projectFlowRequestDTO.setProjectId(projectFlowExecute.getProjectId());
                projectFlowRequestDTO.setSubStatus(ProjectSubStatusEnum.MISCARRY_CREDIT_AMOUNT);
                projectDubboService.flow(projectFlowRequestDTO);

                // 限制用户借款行
                AddUserLoanLimitRequestDTO addUserLoanLimitRequestDTO = new AddUserLoanLimitRequestDTO();
                addUserLoanLimitRequestDTO.setLimitDays(Integer.valueOf(tradeConfigLocalService.selectTradeConfigByEmun(TradeConfigEnum.CARDNIU_HITCREDITMODELLIMITDAYS)));
                addUserLoanLimitRequestDTO.setLimitType(LibraLoanLimitEnum.CREDIT_AMOUNT_LESS.getLimitType().intValue());
                addUserLoanLimitRequestDTO.setUserId(project.getBorrowUserId());
                addUserLoanLimitRequestDTO.setProductTypeEnum(ProductTypeEnum.enumOf(projectFlowExecute.getProjectType()));
                CompletableFuture.runAsync(() -> userForbidDubboService.addUserLibraLoanLimit(addUserLoanLimitRequestDTO));

                // 结束流程
                projectFlowExecuteService.failEndProjectFlowExecute(projectFlowExecute.getFlowExecuteId(), LoanNodeTypeEnum.LOAN_TE_LIMIT_SCORE_BACK, errMsg.toString());
            // 其他情况人工处理
            }else {
                this.handleNeedApprove(projectFlowExecute);
            }
        } else {
            // 否则进入人工审核
            this.handleNeedApprove(projectFlowExecute);
        }
    }

    /**
     * 获取自动审核状态
     * @param project 标的
     * @return
     */
    private int getAutoAuditSwitchStatus(Project project) {
        return Optional.ofNullable(loanBusinessInfoMapperExt.selectByProductType(project.getType())).map(info -> info.getIsAutoAudit().intValue()).orElse(0);
    }

    /***
     * 获取额度评分
     * @param projectId 借款人ID
     * @return
     */
    private BigDecimal getLimitAmount(long projectId) {
        return Optional.ofNullable(projectCreditAmountMapperExt.selectByPrimaryKey(projectId)).map(ProjectCreditAmount::getCreditAmount).orElse(BigDecimal.ZERO);
    }

    /**
     * 获取起借金额
     * @param projectTyp
     * @return
     */
    private BigDecimal getMinLoanAmount(Byte projectTyp) {
        LoanBusinessInfo loanBusinessInfo = null;
        if (ProjectTypeEnum.SOCIAL_SECURITY_LOAN.getValue().equals(projectTyp)) {
            loanBusinessInfo = loanBusinessInfoMapperExt.selectByProductType(ProjectTypeEnum.SOCIAL_SECURITY_LOAN.getValue());// 社保借
        }else if (ProjectTypeEnum.CREDIT_CARD_LOAN.getValue().equals(projectTyp)) {
            loanBusinessInfo = loanBusinessInfoMapperExt.selectByProductType(ProjectTypeEnum.CREDIT_CARD_LOAN.getValue());// 信用卡贷授信
        }else if (ProjectTypeEnum.SMALL_LOAN.getValue().equals(projectTyp)) {
            loanBusinessInfo = loanBusinessInfoMapperExt.selectByProductType(ProjectTypeEnum.SMALL_LOAN.getValue());// 小易借
        }else if (ProjectTypeEnum.CONSUME_LOAN.getValue().equals(projectTyp)) {
        	loanBusinessInfo = loanBusinessInfoMapperExt.selectByProductType(ProjectTypeEnum.CONSUME_LOAN.getValue());// 消费借
        }        
        return Optional.ofNullable(loanBusinessInfo).map(LoanBusinessInfo::getMinAmount).orElse(BigDecimal.ZERO);
    }

    /**
     * * 肖像认证人工审核处理
     * @param projectFlowExecute
     */
    private void handleNeedApprove(ProjectFlowExecuteRuntime projectFlowExecute) {
        log.info("标的流程###肖像认证###人工审核, FlowExecuteId={}, projectId={}", projectFlowExecute.getFlowExecuteId(), projectFlowExecute.getProjectId());
        // 人工审核, 修改标的状态 --> 流程结束
        ((ProjectFlowExecutePortraitAuthListener)AopContext.currentProxy()).doWithTransaction(() -> {
            Project project = projectMapperExt.selectByPrimaryKeyForUpdate(projectFlowExecute.getProjectId());
            if (!project.getStatus().equals(ProjectStatusEnum.CHECKING.getStatus())) {
                log.error("标的流程###肖像认证###转人工审核失败, 标的状态异常FlowExecuteId={}, projectId={}, status={}, subStatus={}", projectFlowExecute.getFlowExecuteId(), project.getProjectId(), project.getStatus(), project.getSubStatus());
                this.handleExceptionFLow(projectFlowExecute, true, String.format("标的流程###肖像认证###转人工审核失败, 标的状态异常status=%s, subStatus=%s", project.getStatus(), project.getSubStatus()));
                return null;
            }
            speedLoanService.toManualAudit(project);
            projectFlowExecuteService.successEndProjectFlowExecute(projectFlowExecute.getFlowExecuteId(), LoanNodeTypeEnum.MANPOWER_AUDIT);
            return null;
        });
    }

    /**
     * 肖像认证认证失败或未认证处理 -> 退回并终结流程
     * @param projectFlowExecute
     */
    private void handleNotPassOrNotCertified(ProjectFlowExecuteRuntime projectFlowExecute) {
        log.info("标的流程###肖像认证###认证失败,退回 FlowExecuteId={}, projectId={}", projectFlowExecute.getFlowExecuteId(), projectFlowExecute.getProjectId());
        // 退回 --> 流程终结
        ((ProjectFlowExecutePortraitAuthListener)AopContext.currentProxy()).doWithTransaction(() -> {
            // 检查标的状态
            Project project = projectMapperExt.selectByPrimaryKeyForUpdate(projectFlowExecute.getProjectId());
            if (project == null || ProjectStatusEnum.CHECKING.getStatus() != project.getStatus().byteValue()) {
                throw new BizException("TRD20001");
            }
            // 更新标的状态
            Project updateProject  = new Project();
            updateProject.setProjectId(project.getProjectId());
            updateProject.setStatus(ProjectSubStatusEnum.CHECKING_BACK_PHONEOPERATOR_TIMEOUT.getParentStatusEnum().getStatus());
            updateProject.setSubStatus(ProjectSubStatusEnum.CHECKING_BACK_PHONEOPERATOR_TIMEOUT.getSubStatus());
            // 审核退回流程未结束，不要往标的表写审核时间和审核人
            if (project.getAuditedTime() == null) {
                updateProject.setAuditedTime(new Date());
            }
            updateProject.setUpdateTime(new Date());
            log.info("标的流程###肖像认证###认证失败,退回, 更新标的状态信息，FlowExecuteId={}, project:{}, toSubStatus={}", projectFlowExecute.getFlowExecuteId(), JSON.toJSONString(project), ProjectSubStatusEnum.CHECKING_BACK);
            if (projectMapperExt.updateByPrimaryKeySelective(updateProject) <= 0) {
                log.error("标的流程###肖像认证###认证失败,退回, 更新标的状态失败，FlowExecuteId={}, project:{}, toSubStatus={}", projectFlowExecute.getFlowExecuteId(), JSON.toJSONString(project), ProjectSubStatusEnum.CHECKING_BACK);
                throw  new BizException("TRD20002");
            }
            final String reason = "身份认证肖像比对不通过";
            returnProjectRecordService.insertReturnProjectRecord(
                    projectFlowExecute.getProjectId(),
                    ProjectTypeEnum.getEnum(project.getType()),
                    project.getBorrowUserId(),
                    String.format("退回原因: %s", reason),
                    String.format("projectSubStatus: %s", ProjectSubStatusEnum.CHECKING_BACK_PHONEOPERATOR_TIMEOUT.getSubStatus()),
                    ReturnProjectProcessSchemeEnum.PORTRAIT_AUTH
            );
            projectFlowExecuteService.failEndProjectFlowExecute(projectFlowExecute.getFlowExecuteId(), LoanNodeTypeEnum.PORTRAIT_AUTH, "标的流程###肖像认证###认证失败或未认证###退回");

            // 事务后置处理 - 通知
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
                @Override
                public void afterCommit() {
                    super.afterCommit();
                    try {
                        boolean isCardLoan = project.getType().equals(com.niiwoo.civet.trade.enums.ProjectTypeEnum.CARD_NIU_LOAN.getValue());
                        boolean isFormCardNiu = project.getFromSource().equals(ProjectFromSourceEnum.CARD_NIU.getType());
                        if (isCardLoan && isFormCardNiu) {
                            //设置参数
                            List<Object> smsTemplateParamList = new ArrayList<Object>();
                            smsTemplateParamList.add(reason);
                            //发送短信服务
                            sendSmsDubboService.sendSmsByUserId(project.getBorrowUserId(), SMSTemplateKeyEnum.CARD_NIU_TEMP_SPEEDLOAN_MANUAL_BACK.getKey(), smsTemplateParamList);
                        } else {
                            //站内信
                            UserLetterRequestDTO userLetterRequestDTO = new UserLetterRequestDTO();
                            userLetterRequestDTO.setProjectId(project.getProjectId());
                            userLetterRequestDTO.setToUserId(project.getBorrowUserId());
                            //设置参数
                            List<Object> letterTemplateParamList = new ArrayList<Object>();
                            letterTemplateParamList.add(project.getProjectId());
                            letterTemplateParamList.add(project.getTitle());
                            letterTemplateParamList.add(reason);
                            List<AppButtonDTO> appButtonDTOS = new ArrayList<>();
                            // 按钮  查看借款
                            AppButtonDTO appButtonDTO = new AppButtonDTO();
                            appButtonDTO.setButtonType(AppButtonDTO.ButtonTypeEnum.VIEW_LOAN);
                            appButtonDTO.setLabel(AppButtonDTO.ButtonTypeEnum.VIEW_LOAN.getDesc());
                            //按钮参数
                            Map<String, Object> params = new HashMap<>();
                            params.put("ProjectId", project.getProjectId());
                            params.put("LoadServerUrl", h5BorrowDetailUrl + project.getProjectId());
                            appButtonDTO.setParams(params);
                            appButtonDTOS.add(appButtonDTO);
                            //只发送站内信
                            userLetterRequestDTO.setUserLetterTypeEnum(UserLetterTypeEnum.ONLY_LETTER);
                            //退回模版
                            userLetterRequestDTO.setLetterTemplateEnum(MessageTemplateKeyEnum.LetterTemplateKeyEnum.SPEED_LOAN_MANUAL_BACK);
                            userLetterRequestDTO.setLetterButtonList(appButtonDTOS);
                            userLetterRequestDTO.setLetterTemplateParamList(letterTemplateParamList);
                            log.info("标的流程###肖像认证###认证失败,退回,  FlowExecuteId={}, 站内信请求={}", projectFlowExecute.getFlowExecuteId(), JSON.toJSONString(userLetterRequestDTO));
                            userLetterDubboService.pushLetter(userLetterRequestDTO);
                            //发送短信服务
                            List<Object> smsTemplateParamList = new ArrayList<Object>();
                            smsTemplateParamList.add(reason);
                            log.info("标的流程###肖像认证###认证失败,退回,  FlowExecuteId={}, 短信请求 userId={}, tpl={}, params={}", projectFlowExecute.getFlowExecuteId(), project.getBorrowUserId(), SMSTemplateKeyEnum.TEMP_SPEEDLOAN_MANUAL_BACK, JSON.toJSONString(smsTemplateParamList));
                            sendSmsDubboService.sendSmsByUserId(project.getBorrowUserId(), SMSTemplateKeyEnum.TEMP_SPEEDLOAN_MANUAL_BACK.getKey(), smsTemplateParamList);

                        }
                    } catch (Exception e) {
                        log.error(String.format("标的流程###肖像认证###认证失败###退回, 后置处理异常 FlowExecuteId=%s projectId=%s, message=%s", projectFlowExecute.getFlowExecuteId(), projectFlowExecute.getProjectId(), e.getMessage()), e);
                    }
                }
            });
            return null;
        });
    }

    /**
     * 发起肖像认证
     * @param projectFlowExecute
     * @param remainingTimes
     */
    private void handleReview(ProjectFlowExecuteRuntime projectFlowExecute, int remainingTimes) {
        log.info("标的流程###肖像认证###认证, FlowExecuteId={} projectId={}", projectFlowExecute.getFlowExecuteId(), projectFlowExecute.getProjectId());
//        if (remainingTimes <= 0) {
//            log.info("标的流程###肖像认证###肖像认证剩余次数不足, userId={}", projectFlowExecute.getBorrowUserId());
//            return;
//        }
        FacePlusPortraitAuthRequestDTO requestDTO = new FacePlusPortraitAuthRequestDTO();
        requestDTO.setUserId(projectFlowExecute.getBorrowUserId());
        log.info("标的流程###肖像认证###认证请求, FlowExecuteId={}, data={}", projectFlowExecute.getFlowExecuteId(), JSON.toJSONString(requestDTO));
        FacePlusPortraitAuthResponseDTO authResponseDTO = facePlusPortraitAuthDubboService.auth(requestDTO);
        log.info("标的流程###肖像认证###认证响应, FlowExecuteId={}, data={}", projectFlowExecute.getFlowExecuteId(), JSON.toJSONString(authResponseDTO));
        switch (authResponseDTO.getPortraitAuthStatus()) {
            // 认证通过
            case PASS_CERTIFIED: {
                this.handlePassCertified(projectFlowExecute);
            } break;
            // 不通过
            case NOT_PASS: {
                this.handleNotPassOrNotCertified(projectFlowExecute);
            } break;
            // 默认人工审核
            default: {
                this.handleNeedApprove(projectFlowExecute);
            }
//            default: {
//                log.error("标的流程###肖像认证###认证状态异常2, status=", authResponseDTO.getPortraitAuthStatus());
//                this.handleExceptionFLow(projectFlowExecute, true, String.format("标的流程###肖像认证###认证状态异常2, status=%s", authResponseDTO.getPortraitAuthStatus()));
//            }
        }
    }

    /**
     * 流程异常处理
     * @param projectFlowExecute 流程
     * @param isOver 是否结束
     */
    private void handleExceptionFLow(ProjectFlowExecuteRuntime projectFlowExecute, boolean isOver, String reason) {
        ((ProjectFlowExecutePortraitAuthListener)AopContext.currentProxy()).doWithTransaction(() -> {
            // 直接结束或重试次数超过3次 --> 异常结束流程
            if (isOver || projectFlowExecute.getRetryTimes() >= 3) {
                projectFlowExecuteService.failEndProjectFlowExecute(projectFlowExecute.getFlowExecuteId(), LoanNodeTypeEnum.PORTRAIT_AUTH, reason);
            } else {
                // 否则增加重试次数，等待再次调度
                ProjectFlowExecuteRuntime f = new ProjectFlowExecuteRuntime();
                f.setFlowExecuteId(projectFlowExecute.getFlowExecuteId());
                f.setRetryTimes(projectFlowExecute.getRetryTimes() + 1);
                f.setUpdateTime(new Date());
                projectFlowExecuteRuntimeMapperExt.updateByPrimaryKeySelective(f);
            }
            return null;
        });
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (applicationContext.getParent() == null) {
            this.ctx = applicationContext;
        }
    }

    @Getter
    @Setter
    private static class ExecuteMQ {

        private Long flowExecuteId;

    }

    private void confirmSendMessage(Project updateProjectDTO) {
        //设置参数
        List<Object> smsTemplateParamList = new ArrayList<Object>();
        //发送短信服务
        sendSmsDubboService.sendSmsByUserId(updateProjectDTO.getBorrowUserId(), SMSTemplateKeyEnum.SMS_USER_CONFIRM_PUSH_PROJECT.getKey(), smsTemplateParamList);
        //站内信
        UserLetterRequestDTO userLetterRequestDTO = new UserLetterRequestDTO();
        userLetterRequestDTO.setUserLetterTypeEnum(UserLetterTypeEnum.ONLY_LETTER);
        userLetterRequestDTO.setLetterTemplateEnum(MessageTemplateKeyEnum.LetterTemplateKeyEnum.LETTER_USER_CONFIRM_PUSH_PROJECT);
        //设置参数
        List<Object> letterTemplateParamList = new ArrayList<Object>();
        letterTemplateParamList.add(updateProjectDTO.getProjectId());
        letterTemplateParamList.add(updateProjectDTO.getTitle());
        //按钮列表
        List<AppButtonDTO> appButtonDTOS = new ArrayList<>();
        AppButtonDTO appButtonDTO = new AppButtonDTO();
        appButtonDTO.setButtonType(AppButtonDTO.ButtonTypeEnum.WAIT_CONFIRM);
        appButtonDTO.setLabel(AppButtonDTO.ButtonTypeEnum.WAIT_CONFIRM.getDesc());
        Map<String, Object> params = new HashMap<>();
        params.put("ProjectId", updateProjectDTO.getProjectId());
        params.put("LoadServerUrl", h5BorrowDetailUrl + updateProjectDTO.getProjectId());
        appButtonDTO.setParams(params);
        appButtonDTOS.add(appButtonDTO);
        userLetterRequestDTO.setLetterTemplateParamList(letterTemplateParamList);
        userLetterRequestDTO.setProjectId(updateProjectDTO.getProjectId());
        userLetterRequestDTO.setToUserId(updateProjectDTO.getBorrowUserId());
        userLetterRequestDTO.setLetterButtonList(appButtonDTOS);
        log.info("标准产品通过待确认userLetterRequestDTO:{}", JSON.toJSONString(userLetterRequestDTO));
        userLetterDubboService.pushLetter(userLetterRequestDTO);
    }

}
